require "mj"
require "chairaction"
require "skynet.manager"
local skynet = require "skynet"


local FANWAIFAN = {QINGYISE=0x0001,HAIDIHU=0x0002,HAIDIPAO=0x0004,GANGHUA=0x0008,GANGPAO=0x0010,QUANQIUREN=0x0020,QIANGGANGHU=0x0040,TIANHU=0x0080,DIHU=0x0100,MENQINIG=0x0200,QUANQIURENDIANPAO=0x0400,SHISANYAO=0x0800,ZIYISE=0x1000}

local TABLECONST = {FULL_PLAYER=4, MAX_COUNT=14,NUM_HANDFULL=14,MAX_TING=13, MAX_MA=10, MAXWEAVCOUNT=4, MAXOUTCARDCOUNT=39, PAI_TOTAL=136}
local HUTYPE = {HT_NULL=0, HT_PINGHU=1, HT_PENGPENGHU=0x2, HT_QIXIAODUI=0x4, HT_QIDADUI=0x8, HT_HAOQI=0xF, HT_CHAOHAOQI=0xF0, HT_SHISANYAO=0x10}
local chairAction1 = {UserChairID=0, tabCbCardIndex = {},tabCbOutCard = {}, tabWeaveItem = {}, tabUserAction = {}, tabTingCardData={} }
local chairAction2 = {UserChairID=1, tabCbCardIndex = {},tabCbOutCard = {}, tabWeaveItem = {}, tabUserAction = {}, tabTingCardData={} }
local chairAction3 = {UserChairID=2, tabCbCardIndex = {},tabCbOutCard = {}, tabWeaveItem = {}, tabUserAction = {}, tabTingCardData={} }
local chairAction4 = {UserChairID=3, tabCbCardIndex = {},tabCbOutCard = {}, tabWeaveItem = {}, tabUserAction = {}, tabTingCardData={} }
local chairActs = {chairAction1, chairAction2, chairAction3, chairAction4}



local CMD = require "game_proto"
local MainCmdId = CMD.Main_ClientAndGameSvr_TableModle
TableLogic = {chairAddr={},chairAct={}, chairIndex={}, PlayerNum=4, Banker=0, CurUserId=0, LeftCardCount=TABLECONST.PAI_TOTAL,              PingHuZiMo=false}

TableLogic.CanChi = false   --是否支持吃

local nGameJuIndex = 1    --第几局

local GangScore = {}

GangScore[WIKTYPE.WIK_ANGANG]  = 1
GangScore[WIKTYPE.WIK_JIAGANG] = 1
GangScore[WIKTYPE.WIK_JIEGANG] = 1

local ZiMoHuScore = {}
ZiMoHuScore[HUTYPE.HT_PINGHU]     = 3
ZiMoHuScore[HUTYPE.HT_PENGPENGHU] = 6
ZiMoHuScore[HUTYPE.HT_QIXIAODUI]  = 8
ZiMoHuScore[HUTYPE.HT_QIDADUI]    = 16
ZiMoHuScore[HUTYPE.HT_HAOQI]      = 32
ZiMoHuScore[HUTYPE.HT_CHAOHAOQI]  = 64
ZiMoHuScore[HUTYPE.HT_SHISANYAO]  = 36


local DianPaoHuScore = {}
DianPaoHuScore[HUTYPE.HT_PINGHU]     = 2
DianPaoHuScore[HUTYPE.HT_PENGPENGHU] = 9
DianPaoHuScore[HUTYPE.HT_QIXIAODUI]  = 12
DianPaoHuScore[HUTYPE.HT_QIDADUI]    = 24
DianPaoHuScore[HUTYPE.HT_HAOQI]      = 48
DianPaoHuScore[HUTYPE.HT_CHAOHAOQI]  = 96
DianPaoHuScore[HUTYPE.HT_SHISANYAO]  = 48

local FanScore = {}
FanScore[FANWAIFAN.QINGYISE] = 2
FanScore[FANWAIFAN.QINGYISE] = 2


function TableLogic:RegisterFun()
    self.actFun = {}
    self.actFun[WIKTYPE.WIK_LINE]    = TableLogic.DoCommonActReq
    self.actFun[WIKTYPE.WIK_PENG]    = TableLogic.DoCommonActReq
    self.actFun[WIKTYPE.WIK_JIAGANG] = TableLogic.DoGangActReq
    self.actFun[WIKTYPE.WIK_JIEGANG] = TableLogic.DoGangActReq
    self.actFun[WIKTYPE.WIK_ANGANG]  = TableLogic.DoGangActReq
    --self.actFun[WIKTYPE.WIK_HU]      = TableLogic.DoHuReq
end

function TableLogic:MakeReadyTable()   
    self.CurUserId = 0
    self.LeftCardCount = TABLECONST.PAI_TOTAL
    self.HasDoHu = false
    self.HasConclude = false
    self.tabGang = {}
    for chair in self:ChairIterator() do
        chair:MakeReadyChair(self)
    end
end

function TableLogic:OneJuOver()
    self.TableObj.m_wCurJu = self.TableObj.m_wCurJu + 1 
end

function TableLogic:InitChair(num)
    self.PlayerNum = num
	if num == 4 then self.chairIndex = {1,2,3,4} end
	if num == 3 then self.chairIndex = {1,2,3} end
	if num == 2 then self.chairIndex = {1,3} end
	for i = 1, #self.chairIndex do
		local index = self.chairIndex[i]
		self.chairAct[index] = ChairAction:New(chairActs[index])
		self.chairAct[index]:Init()
	end
end

function TableLogic:GetChair(chairIndex)
	return self.chairAct[self.chairIndex[chairIndex]]
end

function TableLogic:GetNextChairId(wCurId)   --返回下一个玩家的ID,用在判断吃
    wCurId = (wCurId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER;
	if self.PlayerNum == 2 then
		if wCurId == 1 or wCurId == 3 then
			wCurId = (wCurId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER;
		end
	elseif self.PlayerNum == 3 then
		if wCurId == 3 then
			wCurId = (wCurId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER;
		end
	end
	return wCurId;
end

function TableLogic:UserIdToChair(wUserChairId)    --userId 是以0开始的,而不是1
    local chairIndex = wUserChairId + 1
    return self.chairAct[chairIndex]
end

function TableLogic:ChairIterator(wCurId)
    local i = 0
    return function()
        i = i + 1
        if i <= self.PlayerNum then 
            local chair = self:GetChair(i)
            if wCurId and wCurId == chair.UserChairID then return nil end    --忽略当前玩家
            if chair and chair.addr then return chair
            else return nil end    --退出后地址就为nil
        end
    end
end

function TableLogic:PlayerToChair(Player)      --地址到椅子
    return TableLogic.chairAddr[Player.nAgentAddr]
end

function TableLogic:StandUp(chair)
    for chair in self:ChairIterator() do 
        local msg = string.pack('<I2', chair.UserChairID)
        print('quit '..chair.UserChairID)
        chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_PlayerQuit, msg)
        chair:Quit()
    end
end

function TableLogic:BroadCastGameStart()
    for chair in self:ChairIterator() do
        chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_GameStart)
	end
    MjLogic:Init()
    MjLogic:randCard(1)
    self:RandomFromConfig(MjLogic.table_RepertoryCard)
    self:StartFaPai(nGameJuIndex)
    nGameJuIndex = nGameJuIndex + 1
end

function TableLogic:RandomFromConfig(tab_RandCard)
    local logicPath = skynet.getenv('gamelogic_path')
    if not logicPath then return end
    skynet.error('logicPath is'..logicPath)
    local fileFaPai = io.open(logicPath..'FaPai.lua',rb)
    if fileFaPai then fileFaPai:close() 
    else skynet.error('no fapai file!!!') return end
    local Table = require("FaPai")
    if not Table.Enable then return end     --没有开启
    if not Table.endPai then Table.endPai = {} end
    local cuRepertoryCard = {}
    for i = 1, TABLECONST.PAI_TOTAL do
        cuRepertoryCard[i] = 0
    end
    local cbLeftCardCount = TABLECONST.PAI_TOTAL
    local pai = {Table.pai_1, Table.pai_2, Table.pai_3, Table.pai_4, Table.pai_5}
    if self.PlayerNum == 2 then 
        pai = {Table.pai_1, Table.pai_2, Table.pai_5}
    elseif self.PlayerNum == 3 then 
        pai = {Table.pai_1, Table.pai_2, Table.pai_3, Table.pai_5}
    end
    local iStart = 0
    for k = 1, #pai do
         for i = 1, TABLECONST.MAX_COUNT - 1 do
            cuRepertoryCard[cbLeftCardCount] = pai[k][i]
            if not cuRepertoryCard[cbLeftCardCount] then cuRepertoryCard[cbLeftCardCount] = 0 end
            cbLeftCardCount = cbLeftCardCount - 1
            iStart = iStart + 1
        end
    end
    for i = 1, #Table.endPai do
        cuRepertoryCard[i] = Table.endPai[i]
    end
    cbLeftCardCount = TABLECONST.PAI_TOTAL
    for i = TABLECONST.PAI_TOTAL, 1, -1 do
        if i > (cbLeftCardCount - iStart) and cuRepertoryCard[i] > 0 then 
            for j = 1, cbLeftCardCount do
                if tab_RandCard[j] == cuRepertoryCard[i] then 
                    tab_RandCard[cbLeftCardCount], tab_RandCard[j] = tab_RandCard[j], tab_RandCard[cbLeftCardCount]
                    cbLeftCardCount = cbLeftCardCount - 1
                    cuRepertoryCard[i] = 0
                    break
                end
            end
        else break
        end
    end
    
    cbLeftCardCount = TABLECONST.PAI_TOTAL
    local iEnd = #Table.endPai
    for i = 1, TABLECONST.PAI_TOTAL do
        if i < iEnd and cuRepertoryCard[i] > 0 then 
            for j = i, cbLeftCardCount - iStart do
                if tab_RandCard[j] == cuRepertoryCard[i] then
                    tab_RandCard[i], tab_RandCard[j] = tab_RandCard[j], tab_RandCard[i]
                    cuRepertoryCard[i] = 0
                    break
                end
            end
        else break
        end
    end
    return tab_RandCard
end

function TableLogic:StartFaPai(wJu)
    local tab_RandCard = MjLogic.table_RepertoryCard
    self.cardRepertory = tab_RandCard
	self.LeftCardCount = TABLECONST.PAI_TOTAL
    for i = 1, self.PlayerNum do
        local chair = self:GetChair(i)		
		for j = 1, TABLECONST.NUM_HANDFULL - 1 do		
			local val = tab_RandCard[self.LeftCardCount]
			chair.tabCardVal[#chair.tabCardVal + 1] = val
			local valIndex = MjLogic:ValToIndex(val)
			chair.tabCbCardIndex[valIndex] = chair.tabCbCardIndex[valIndex] + 1
			self.LeftCardCount = self.LeftCardCount - 1
		end
	end
    
    for chair in self:ChairIterator() do
        chair:StartFaPai(wJu)
        chair:UpdataTingData()
	end
    self.CurUserId = self.Banker
    self:DispatchCard(self:UserIdToChair(self.CurUserId))
end

function TableLogic:BroadCastData(subId, dataBuf)
    for chair in self:ChairIterator() do
		chair:SendDataInLua(subId, dataBuf)
	end
end

function TableLogic:DispatchCard(chair, type)    --摸牌
    if self:CheckLiuJu() then 
        self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_ChouZhang)
        return 
    end
    if chair == nil then chair = self:UserIdToChair(self.CurUserId) end
	local getCard = self.cardRepertory[self.LeftCardCount]
    --getCard = 0x04
	self.LeftCardCount = self.LeftCardCount - 1
    self.CurGetCard = getCard   --玩家当前的牌,用于断线重连
	chair:OnDispatchCard(getCard, self.LeftCardCount)
    self.CurUserId = chair.UserChairID
	self:DispatchCardToOthers(chair, getCard)
    --chair:CheckHu(getCard)
    if type == WIKTYPE.WIK_JIEGANG or
       type == WIKTYPE.WIK_JIAGANG or
       type == WIKTYPE.WIK_ANGANG then 
        
    else      --不是因为杠摸牌,连杠置为0
        chair.LianGang = 0
    end
end

function TableLogic:DispatchCardToOthers(chair, getCard)    --发牌给别人
	local wUserChairId = chair.UserChairID
    for chair in self:ChairIterator() do
        if chair.UserChairID ~= wUserChairId then 
			chair:OthersCardToMe(wUserChairId, self.LeftCardCount)
		end
    end
end


function TableLogic:BroadCastOutCard()
    
end

function TableLogic:SetNextUser()
	self.CurUserId = (self.CurUserId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER
	self:ValidCurrentUserChairId()
end

function TableLogic:ValidCurrentUserChairId()       --修正2,3人麻将下一个玩家的问题
	if self.PlayerNum == 2  then
		if self.CurUserId == 1 or self.CurUserId == 3 then
			self.CurUserId = (self.CurUserId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER
		end
	elseif self.PlayerNum == 3 then
		if self.CurUserId == 3 then
			self.CurUserId = (self.CurUserId + TABLECONST.FULL_PLAYER + 1) % TABLECONST.FULL_PLAYER
		end
	end
	return self.CurUserId
end

function TableLogic:OnOutCard(chair, cbCardData)
    local wUserChairId = chair.UserChairID
	chair:OnOutCard(cbCardData)
    chair.LianGang = 0
    self.LastOutCardId = wUserChairId
    self.CurGetCard = 0
    self.CurUserId = wUserChairId
	self:UpdataTingData(chair)
    local tabCouldHu = {}
	local hasPengOrGang = false
    local hasChi = false
    
    local dataToSend = string.pack('<I2B', wUserChairId, cbCardData)
    for chair in self:ChairIterator() do  
        chair.OperateBit = 0
        local couldHu, couldPengOrGang = false, false
		if chair.UserChairID ~= wUserChairId then
			couldHu = chair:CanHu(cbCardData, wUserChairId)
			couldPengOrGang = chair:CanPeng(cbCardData, wUserChairId)
            if couldPengOrGang then chair.OperateBit = chair.OperateBit|WIKTYPE.WIK_PENG end
            local bGang = chair:CanGang(cbCardData, WIKTYPE.WIK_JIEGANG, wUserChairId)
            if bGang then chair.OperateBit = chair.OperateBit|WIKTYPE.WIK_JIEGANG end
            couldPengOrGang = bGang or couldPengOrGang
			if couldHu then table.insert(tabCouldHu, chair.UserChairID) chair.OperateBit = chair.OperateBit|WIKTYPE.WIK_HU end
            
            if chair.UserChairID == self:GetNextChairId(wUserChairId) then   --检测吃
                hasChi = chair:CanChi(cbCardData, wUserChairId)
                if hasChi then chair.OperateBit = chair.OperateBit|WIKTYPE.WIK_LINE end
            end
		end
		if couldPengOrGang then hasPengOrGang = true end
		local hp = 0
		if couldPengOrGang then hp = 1 end
        local hChi = 0
        if hasChi then hChi = 1 end
        dataToSend = dataToSend..string.pack('BB', hp, hChi)
        chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_UserOutCard, dataToSend)
    end
    
    local hasPGH = false
    for chair in self:ChairIterator() do  
        if chair.UserChairID ~= wUserChairId then
			if (chair.OperateBit&(WIKTYPE.WIK_PENG|WIKTYPE.WIK_HU)) > 0 then hasPGH = true chair:SendOperateHint() end
		end
    end
    
    for chair in self:ChairIterator() do  
        if chair.UserChairID ~= wUserChairId and not hasPGH then
			if (chair.OperateBit&(WIKTYPE.WIK_LINE)) > 0 then chair:SendOperateHint() end
		end
    end
    
    if (not hasPengOrGang) and (#tabCouldHu == 0) and (not hasChi) then 
		self:SetNextUser()
		self:DispatchCard()
	end
end

function TableLogic:UpdataTingData(chair)
    chair:UpdataTingData()
end

function TableLogic:DoHuReq(chair, getCardData)
	if self.HasDoHu then return end    --防止重复
	local bHu, huType, fan, provider = chair:DoHu(getCardData)
	if bHu then
        self:DoHuCmd(chair, provider, getCardData, huType, fan)
	end
	self.HasDoHu = true
end

function TableLogic:DoHuCmd(chair, provider, getCardData, huType, fan)
    local bZiMo = false
    if provider and provider ~= -1 then 
        self:UserIdToChair(provider).DianPao = true
    else bZiMo = true end
    fan = fan | self:GetFanWaiFan(chair)
    self.HuCardData = getCardData
    local bufData = string.pack('<I2B<IIIIIIIIII', chair.UserChairID, huType, fan&FANWAIFAN.QINGYISE, fan&FANWAIFAN.GANGPAO, 
    fan&FANWAIFAN.GANGHUA, fan&FANWAIFAN.HAIDIPAO, fan&FANWAIFAN.HAIDIHU, fan&FANWAIFAN.QIANGGANGHU, 
    fan&FANWAIFAN.QUANQIUREN, fan&FANWAIFAN.TIANHU, fan&FANWAIFAN.DIHU, fan&FANWAIFAN.ZIYISE)

    self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_HuPai, bufData)
    chair.tabHuResult = {ProviderId = provider, HuCardData = getCardData, HuType = huType, Fan = fan}
    
    local HuData = {HuId = chair.UserChairID, ProviderId = provider, HuCardData = getCardData, HuType = huType, ZiMo = bZiMo, Fan = fan}
    skynet.call(self.ItableSrv, "lua", "ConcludeGame", self.TableObj, HuData)
end

function TableLogic:GetFanWaiFan(chair)
	return chair:GetFanWaiFan()
end

function TableLogic:CheckQiangGangHu(chairGang, card)
    local oneHu = 0
    for chair in self:ChairIterator() do
        if chair.UserChairID ~= chairGang.UserChairID then 
            if chair:CanHu(card, chairGang.UserChairID) then 
                chair.OperateBit = WIKTYPE.WIK_HU
                local msg = chair.tabUserAction:GetPackString()
                chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_CanDianPaoHu, msg)
                oneHu = oneHu + 1
            end
        end
    end
    return oneHu
end

function TableLogic:OtherChairsHasMoreAct(chairAct, type)
    local oneAct = 0
    for otherChair in self:ChairIterator() do
        if otherChair.UserChairID ~= chairAct.UserChairID then
            if type*otherChair.OperateBit > 0 and otherChair.OperateBit >= type then
                oneAct = oneAct + 1
            end
        end
    end
    return oneAct
end

function TableLogic:DoOthersHigherAct(chairAct, type)
    for otherChair in self:ChairIterator() do
        if otherChair.UserChairID ~= chairAct.UserChairID then
            if otherChair.tabToDo and otherChair.tabToDo.tabParam then 
                local param = otherChair.tabToDo.tabParam
                if param[1] > type then 
                    otherChair.tabToDo.Func(otherChair, param[1], param[2], param[3], param[4])
                    otherChair.tabToDo = nil
                    return true
                end
            end 
        end
    end
    return false
end

function TableLogic:WaitForOtherActs(chairAct, type, centerCard, cardBef, cardAft, provider)
    local otherActNum = self:OtherChairsHasMoreAct(chairAct, type)
    if otherActNum > 0 then
        chairAct:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_WaitingForAnotherHu)
        chairAct.tabToDo = {}
        chairAct.tabToDo.Num = otherActNum
        chairAct.tabToDo.Func = chairAct.DoDelayAction
        chairAct.tabToDo.tabParam = {type, {centerCard, cardBef, cardAft}, provider}
        chairAct.OperateBit = 0
        return true 
    end
    return false
end

function TableLogic:DoActionReq(chair, type, centerCard, cardBef, cardAft)
    if type == WIKTYPE.WIK_HU then 
        chair.DoneHu = true
        if self:WaitForOtherActs(chair, type, centerCard, cardBef, cardAft) then    --点胡了,但是因为有其他玩家也胡,暂时胡不了
            local bHu, huType, fan, provider = chair:DoHu(centerCard)
            if bHu then
                fan = fan|self:GetFanWaiFan(chair) 
                chair.tabHuResult = {ProviderId = provider, HuCardData = centerCard, HuType = huType, Fan = fan}
            end
        else 
            self:DoHuReq(chair, centerCard)    --只有一家胡
        end
    else 
        if self.actFun[type] then self.actFun[type](self, type, chair, centerCard, cardBef, cardAft) end
    end    
end

function TableLogic:DoCommonActReq(type, chair, centerCard, cardBef, cardAft)      --吃,碰
    local provider = chair:DoActionReq(chair.DoMsgList[type], centerCard, cardBef, cardAft)
    if provider then 
        self:SetNextUser()
        self:DoActionCmd(type, chair, provider, centerCard, cardBef, cardAft)
    end
end

function TableLogic:DoGangActReq(type, chair, centerCard)      --杠
    local provider = chair:DoActionReq(chair.DoMsgList[type], centerCard) 
    if provider then
        if type == WIKTYPE.WIK_JIEGANG then self:SetNextUser() end  --接杠
        self:DoActionCmd(type, chair, provider, centerCard)
        self:DispatchCard(chair, type)
    end
end

function TableLogic:DoActionCmd(type, chair, provider, centerCard, cardBef, cardAft)
    local wUserChairId = chair.UserChairID
    local dataBuf = ''
    
    function FillGangFen(type)
        local dataBuf = string.pack('<I2B', wUserChairId, centerCard)
        for i = 1, TABLECONST.FULL_PLAYER do
            local chairIn = self.chairAct[i]
            if chairIn then 
                if type == WIKTYPE.WIK_ANGANG then 
                    if chairIn.UserChairID == chair.UserChairID then dataBuf = dataBuf..string.pack('i', self:GetPerGangFen(type) * (self.PlayerNum - 1))
                    else dataBuf = dataBuf..string.pack('i', self:GetPerGangFen(type) * -1) end
                elseif type == WIKTYPE.WIK_JIAGANG then 
                    if chairIn.UserChairID == chair.UserChairID then dataBuf = dataBuf..string.pack('i', self:GetPerGangFen(type))
                    elseif chairIn.UserChairID == provider then dataBuf = dataBuf..string.pack('i', self:GetPerGangFen(type) * -1) 
                    else dataBuf = dataBuf..string.pack('i', 0) end
                end
            else 
                dataBuf = dataBuf..string.pack('i', 0) 
            end
        end
        return dataBuf
    end
    
    if type == WIKTYPE.WIK_PENG then 
		dataBuf = dataBuf..string.pack('<I2B<I2<I2', wUserChairId, centerCard, provider, wUserChairId)
		dataBuf = dataBuf..chair.tabUserAction:GetPackString()
        self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_UserDoPengOk, dataBuf)
        
    elseif type == WIKTYPE.WIK_LINE then 
		dataBuf = dataBuf..string.pack('<I2B<I2<I2BB', wUserChairId, centerCard, provider, wUserChairId, cardBef, cardAft)
        chair.tabUserAction:Reset()
		dataBuf = dataBuf..chair.tabUserAction:GetPackString()
        self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_UserDoChiOk, dataBuf)
        
    elseif type == WIKTYPE.WIK_JIEGANG then 
        local bufData = string.pack('<I2B<I2IIII', wUserChairId, centerCard, provider, 0,0,0,0)
		self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_UserDoJieGangOk, bufData)
        
    elseif type == WIKTYPE.WIK_JIAGANG then 
        local dataBuf = FillGangFen(type)
        self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_UserDoJiaGangOk, dataBuf)
        
    elseif type == WIKTYPE.WIK_ANGANG then 
        local dataBuf = FillGangFen(type)
        self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_UserDoAnGangOk, dataBuf)
    end
    
end

function TableLogic:CancelActionReq(chair, type)
    if chair:CancelActionReq(type) then      --取消成功,可能会重复发送消息
        chair.OperateBit = 0
        
        for otherChair in self:ChairIterator() do        --取消时其他玩家可能还有操作未被执行,例如有玩家1碰,玩家2胡,2取消
            if otherChair.UserChairID ~= chair.UserChairID then
                if otherChair.tabToDo and otherChair.tabToDo.tabParam then 
                    if not otherChair.tabToDo.Num then otherChair.tabToDo.Num = 1 end   
                    otherChair.tabToDo.Num = otherChair.tabToDo.Num - 1 
                    local param = otherChair.tabToDo.tabParam    --type, tabCardData, provider
                    if otherChair.tabToDo.Num == 0 or self:OtherChairsHasMoreAct(otherChair, param[1]) == 0 then 
                        otherChair.tabToDo.Func(otherChair, param[1], param[2], param[3])
                        otherChair.tabToDo = nil
                    end
                    return
                end 
            end            
        end
       
        for otherChair in self:ChairIterator() do    --其他玩家没有同样的动作(一般是胡)要处理,有多响时要检测
            if otherChair.UserChairID ~= chair.UserChairID and #otherChair.tabUserAction > 0 then
                for i = 1, #otherChair.tabUserAction do
                    if otherChair.tabUserAction[i].Type == type then return end
                end
            end
        end
        
        for chair in self:ChairIterator() do       --其他玩家有动作,比如取消点炮,可能下个玩家有吃(因为有胡,吃时,吃没有显示出来)
            if #chair.tabUserAction > 0 then 
                self.CurUserId = self.LastOutCardId
                chair:SendOperateHint()
                return 
            end
        end
           
        if self.CurUserId ~= chair.UserChairID then   --取消暗杠,加杠,自摸胡不需要转移到其他玩家
            self:SetNextUser()
            self:DispatchCard()
        end
    end
end

function TableLogic:CheckLiuJu()
    local Table = {}
    local logicPath = skynet.getenv('gamelogic_path')
    if logicPath then 
        local fileFaPai = io.open(logicPath..'FaPai.lua',rb)
        if fileFaPai then fileFaPai:close() 
            Table = require("FaPai")
        else skynet.error('no fapai file!!!') end
    end
    local nLeft = 0
    if Table.leftCard and Table.leftCard > 0 then nLeft = TABLECONST.PAI_TOTAL - Table.leftCard - (TABLECONST.MAX_COUNT - 1) * self.PlayerNum end
    if self.LeftCardCount <= nLeft then 
        self.LiuJu = true
        local HuData = {LiuJu = true}
		skynet.call(self.ItableSrv, "lua", "ConcludeGame", self.TableObj, HuData)
		return true
	end
	return false
end

function TableLogic:GetNewBanker(huData)
    local banker = 0
    local nHuerCount = 0
    for chair in self:ChairIterator() do
        if chair.DoneHu then nHuerCount = nHuerCount + 1 end
    end
    banker = huData.HuId
    if nHuerCount > 1 then banker = huData.ProviderId end
    return banker
end

HuPaiResult = {}
function HuPaiResult:New(obj)
	obj = obj or {}
	setmetatable(obj, self)
	self.__index = self
	return obj
end

function HuPaiResult:GetPackString()
    local bufData = string.pack('B', self.CardData or 0)
	for i = 1, TABLECONST.FULL_PLAYER do
		local bHu = 0
		if self.bHu[i] then bHu = 1 end
		bufData = bufData..string.char(bHu)
	end
	for i = 1, TABLECONST.FULL_PLAYER do
		bufData = bufData..string.pack('i4', self.gameScoreJu[i])
	end
	for i = 1, TABLECONST.FULL_PLAYER do
        bufData = bufData..string.char(#self.standCard[i])
	end
	for i = 1, TABLECONST.FULL_PLAYER do
		for j = 1, TABLECONST.MAX_COUNT do
			local standPai = self.standCard[i][j]
			if standPai then bufData = bufData..string.char(standPai)
			else bufData = bufData..string.char(0) end
		end
	end
	for i = 1, TABLECONST.FULL_PLAYER do
        bufData = bufData..string.char(#self.arrMa[i])
	end
	for i = 1, TABLECONST.FULL_PLAYER do
		for j = 1, TABLECONST.MAX_MA do
			local ma = self.arrMa[i][j]
			if ma then
                bufData = bufData..string.pack('BB', ma.CardData, ma.Got)
			else bufData = bufData..string.pack('I2', 0) end
		end
	end
	for i = 1, TABLECONST.FULL_PLAYER do
        bufData = bufData..string.char(self.fengHu[i])
	end
	for i = 1, TABLECONST.FULL_PLAYER do
		local len = #self.huPaiMsg[i]
		if len > 0 then 
            local len = string.len(self.huPaiMsg[i])
            bufData = bufData..self.huPaiMsg[i]
            bufData = bufData..BufTool.WriteOffset(256 - len)
		else bufData = bufData..BufTool.WriteOffset(256) end
	end
	return bufData
end

function TableLogic:ConcludeGame(huData)
	if self.HasConclude then return end
    local tabMa = {{},{},{},{}}
    
    local bZiMo = huData.ZiMo
    if not huData.LiuJu then    --没有流局,买马且自摸才买马选项下自摸
        if not self.OnlyZiMoMa or (self.OnlyZiMoMa and bZiMo) then  --且自摸才买马选项下自摸
            for chair in self:ChairIterator() do
                chair.MaGot = 0
                if chair.DoneHu then tabMa[chair.UserChairID], chair.MaGot = self:GalZhongMa(chair.UserChairID) end  
            end
        end
        self.Banker = self:GetNewBanker(huData)     --设置新的庄家
    else
        self.LiuJuNum = (self.LiuJuNum or 0) + 1
    end
    self:CalScore(huData)
     for chair in self:ChairIterator() do
		chair:ConcludeGame(huData)
	end
   
	local huPaiResult = {CardData=0, bHu={false,false,false,false},gameScoreJu={0,0,0,0},standCard={{},{},{},{}}, arrMa={{},{},{},{}},fengHu={0,0,0,0},huPaiMsg={"","","",""}}
	local result = HuPaiResult:New(huPaiResult)
	result.CardData = huData.HuCardData
    
	for chair in self:ChairIterator() do
		if chair then
			local chairIndex = chair.UserChairID + 1
			result.bHu[chairIndex] = chair.DoneHu
			result.gameScoreJu[chairIndex] = chair.GameScoreJu or 0
			result.standCard[chairIndex] = chair.standCard or {}
            result.arrMa[chairIndex] = tabMa[chair.UserChairID] or {}
			result.fengHu[chairIndex] = chair.FengHu or 0
			result.huPaiMsg[chairIndex] = chair.ResultMsg or ""
		end
	end
	local dataBuf = result:GetPackString()
	self:BroadCastData(CMD.Sub_GameSvrToClient_TableModle_CommonHuPaiResult, dataBuf)
	self.HasConclude = true
    
    self:UpdataChairs(huData)
    
    for chair in self:ChairIterator() do
        local chair_obj = self.TableObj.m_pChairs[chair.UserChairID]
        chair_obj.Player.nScore = (chair_obj.Player.nScore or 0) + chair.GameScoreJu
    end
    
    skynet.call(self.ItableSrv, "lua", "ConcludeGameFriend", self.TableObj)
    
    function ClearChairData()
        for chair in self:ChairIterator() do
            chair.GameScoreJu = 0
            chair.standCard = {}
            chair.FengHu = 0
            chair.ResultMsg = ''
            chair.DoneHu = false
            chair.nMingGang = 0
            chair.nAnGang = 0
            chair.tabHuResult = {}
        end
        self.LiuJu = false
    end
    ClearChairData()
    return self.TableObj
end

function TableLogic:ConcluedGameLastRound()
    local dataBuf = ''
    for i = 0, TABLECONST.FULL_PLAYER - 1 do 
        local chair_obj = self.TableObj.m_pChairs[i]
        dataBuf =  dataBuf..string.pack('i4', chair_obj.nScores or 0)  --nGameScoreTotal
    end
    for i = 0, TABLECONST.FULL_PLAYER - 1 do 
        local chair_obj = self.TableObj.m_pChairs[i]
        dataBuf = dataBuf..string.pack('I4', chair_obj.m_nHuPaiTotal or 0)  --nHuPaiTotal
    end
    for i = 0, TABLECONST.FULL_PLAYER - 1 do 
        local chair_obj = self.TableObj.m_pChairs[i]
        dataBuf = dataBuf..string.pack('I4', chair_obj.nMingGang or 0)  --nGongGangTotal
    end
    for i = 0, TABLECONST.FULL_PLAYER - 1 do 
        local chair_obj = self.TableObj.m_pChairs[i]
        dataBuf = dataBuf..string.pack('I4', chair_obj.nAnGang or 0)  --nAnGangTotal
    end
    for i = 0, TABLECONST.FULL_PLAYER - 1 do 
        local chair_obj = self.TableObj.m_pChairs[i]
        dataBuf = dataBuf..string.pack('I4', chair_obj.nDiaoPao or 0)  --nDianPaoTotal
    end
           
    dataBuf = dataBuf..string.pack('I4', self.LiuJuNum or 0)     --nLiuJu
    local endtime = tostring(os.date("%Y-%m-%d %H:%M:%S",os.time()))
    dataBuf = dataBuf..endtime
    dataBuf = dataBuf..BufTool.WriteOffset(32 - string.len(endtime))

    for chair in self:ChairIterator() do
        chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_8JuGameOver, dataBuf)
    end
    
end

function TableLogic:CalScore(huData)
    function ClearScore()
        for chair in self:ChairIterator() do
            chair.GameScoreJu = 0
            chair.GangScoreJu = 0
        end
    end
    ClearScore()
    
    function CalZiMoPerScore(nScorePer, nBei)   --适用于求自摸时每家人的得分,不包括包赔场景
        for chair in self:ChairIterator() do
            if chair.DoneHu then     --自摸者
                chair.GameScoreJu = nScorePer * (self.PlayerNum - 1) * nBei
            else 
                chair.GameScoreJu = nScorePer * (-1) * nBei
            end
        end
    end
    
    if not huData or huData.LiuJu then self:CalGangScore() return end   --流局
    
    local bZiMo = huData.ZiMo
    local nScorePer = self:CalPerScore(bZiMo, huData.HuType, huData.Fan)
    if bZiMo then     --自摸
        local nBei = self:UserIdToChair(huData.HuId):GetFanBeiScore()
        if huData.Fan&FANWAIFAN.GANGHUA > 0 then    --杠花
            local bBaoPei, baiPeiId = self:CalGangHuaLoser(huData.HuId)
            if bBaoPei then 
                self:UserIdToChair(huData.HuId).GameScoreJu = nScorePer * (self.PlayerNum - 1) * nBei
                self:UserIdToChair(baiPeiId).GameScoreJu = nScorePer * (self.PlayerNum - 1) * nBei * (-1)
            else 
                CalZiMoPerScore(nScorePer, nBei)
            end
        else
            CalZiMoPerScore(nScorePer, nBei)
        end
    else   --点炮
        local lost = 0
        for chair in self:ChairIterator() do
            if chair.DoneHu then     --接炮(胡)者
                local nBei = chair:GetFanBeiScore()
                chair.GameScoreJu = nScorePer * nBei
                lost = lost + chair.GameScoreJu
            end
        end
        self:UserIdToChair(huData.ProviderId).GameScoreJu = (-1) * lost
    end
    
    self:CalGangScore()
end

function TableLogic:GalZhongMa(HuId)
    --self.Banker = 0
    local maxMaCount = TableLogic.MaiMaCount or 0
    local tabMa = {}
    local got = 0
    for i = 1, maxMaCount do
        tabMa[i] = {}
        local card = self.cardRepertory[self.LeftCardCount - i + 1]
        tabMa[i].CardData = card
        tabMa[i].Got = 0
        if (card&0x0F % 4) - 1 == (HuId + TABLECONST.FULL_PLAYER - self.Banker) % 4 then
            tabMa[i].Got = 1
            got = got + 1
        end
    end
    return tabMa, got
end

function TableLogic:CalGangHuaLoser(HuId)     --计算杠花提供者,暗杠有可能是明杠者提供的
    local n = #self.tabGang
    local tabHuIdGang = {}
    for i = 1, n do
        local  gang = self.tabGang[i]
        if gang.Doer == HuId then 
            tabHuIdGang[#tabHuIdGang] = gang
        end
    end
    local index = n - (self:UserIdToChair(HuId).LianGang or 0) + 1  --如果有连杠,看是第几个杠引起的
    if index > n then index = n end
    if tabHuIdGang[index].Type == WIKTYPE.WIK_ANGANG then 
        return false, - 1
    else  
        return true, tabHuIdGang[index].Provider
    end
end

function TableLogic:GetPerGangFen(type)
    return GangScore[type] * (self.DiFen or 1)
end

function TableLogic:CalGangScore()
    local n = #self.tabGang
    for i = 1, n do
        local  gang = self.tabGang[i]
        if gang.Type == WIKTYPE.WIK_ANGANG then
            self:UserIdToChair(gang.Doer).GangScoreJu = self:UserIdToChair(gang.Doer).GangScoreJu + self:GetPerGangFen(gang.Type) * (self.PlayerNum - 1)
            for chair in self:ChairIterator() do  --暗杠都减
                if chair.UserChairID ~= gang.Doer then 
                    chair.GangScoreJu = chair.GangScoreJu - self:GetPerGangFen(gang.Type)
                end
            end
        elseif gang.Type == WIKTYPE.WIK_JIEGANG then
            self:UserIdToChair(gang.Doer).GangScoreJu = self:UserIdToChair(gang.Doer).GangScoreJu + self:GetPerGangFen(gang.Type)
            self:UserIdToChair(gang.Provider).GangScoreJu = self:UserIdToChair(gang.Provider).GangScoreJu - self:GetPerGangFen(gang.Type)
        elseif gang.Type == WIKTYPE.WIK_JIAGANG then
            self:UserIdToChair(gang.Doer).GangScoreJu = self:UserIdToChair(gang.Doer).GangScoreJu + self:GetPerGangFen(gang.Type) * (self.PlayerNum - 1)
            for chair in self:ChairIterator() do  --加杠都减
                if chair.UserChairID ~= gang.Doer then 
                    chair.GangScoreJu = chair.GangScoreJu - self:GetPerGangFen(gang.Type)
                end
            end
        end
    end
    
    for chair in self:ChairIterator() do  --加杠都减
        chair.GameScoreJu = chair.GameScoreJu + chair.GangScoreJu
        self.TableObj.m_pChairs[chair.UserChairID].nScore = chair.GameScoreJu
    end
end

function TableLogic:CalPerScore(bZiMo, huType, huFan)
    local baseFen = 0
    if bZiMo then 
        baseFen = ZiMoHuScore[huType] or ZiMoHuScore[HUTYPE.HT_PINGHU]   --自摸门清都*1.333333
        if huFan&FANWAIFAN.MENQINIG > 0 then baseFen = baseFen/3*4 end
    else 
        if huType == HUTYPE.HT_PINGHU then 
            baseFen = DianPaoHuScore[huType] or DianPaoHuScore[HUTYPE.HT_PINGHU]
        else    --如果是平胡门清,还是2分,非平胡门清就要*1.33333
            baseFen = DianPaoHuScore[huType] or DianPaoHuScore[HUTYPE.HT_PINGHU]
            if huFan&FANWAIFAN.MENQINIG > 0 then baseFen = baseFen/3*4 end
        end
    end
    if not bZiMo then    --点炮
        if huType == HUTYPE.HT_PINGHU then 
            if self.PingHuZiMo then    --在“平胡需自摸”的规则下
                if huFan&(FANWAIFAN.HAIDIPAO|FANWAIFAN.GANGPAO|FANWAIFAN.QIANGGANGHU|FANWAIFAN.QUANQIUREN|FANWAIFAN.QUANQIURENDIANPAO) > 0 then
                    baseFen = 9
                    if (huFan & FANWAIFAN.MENQINIG) > 0 then baseFen = 12 end 
                end
            else
                if huFan&(FANWAIFAN.HAIDIPAO|FANWAIFAN.GANGPAO|FANWAIFAN.QIANGGANGHU|FANWAIFAN.QUANQIUREN|FANWAIFAN.QUANQIURENDIANPAO) > 0 then
                    baseFen = 9
                end
            end
        end
    end
    if huFan&(FANWAIFAN.TIANHU|FANWAIFAN.DIHU) > 0 then baseFen = baseFen + 32 end  --12.“天胡”“地胡”，被扣分的人在原有扣分基础上，所扣分数+32
    if self.PingHuZiMo then 
        if huFan&(FANWAIFAN.TIANHU) > 0 then baseFen = 4 + 32 end
        if huFan&(FANWAIFAN.TIANHU) > 0 then baseFen = 12 + 32 end
    else 
        if huFan&(FANWAIFAN.TIANHU) > 0 then baseFen = 4 + 32 end
        if huFan&(FANWAIFAN.TIANHU) > 0 then baseFen = 2 + 32 end
    end
    
    return baseFen * (self.DiFen or 1)
end

function TableLogic:UpdataChairs(huData)     --数据填充到平台上,累积每局
    self.TableObj.m_nBanker = self.Banker
    for chair in self:ChairIterator() do
        local chairObj = self.TableObj.m_pChairs[chair.UserChairID]
        if not chairObj then print('chairObj is not ok '..chair.UserChairID) end
        chairObj.nScores = chairObj.nScores + chair.GameScoreJu
        chairObj.nMingGang = chairObj.nMingGang + (chair.nMingGang or 0)
        chairObj.nAnGang = chairObj.nAnGang + (chair.nAnGang or 0)
        if chair.UserChairID == huData.HuId then 
            chairObj.m_nHuPaiTotal = (chairObj.m_nHuPaiTotal or 0) + 1
        end
        if chair.UserChairID == huData.ProviderId then 
            chairObj.nDiaoPao = (chairObj.nDiaoPao or 0) + 1
        end
    end
end

function TableLogic:SendMeOfflineBackData(Player, wUserChairId) 
    local chair = self:UserIdToChair(wUserChairId)
	local bufData = ''
    bufData = bufData..string.pack('I2I2', self.Banker, self.CurUserId)  --庄家和当前用户
    bufData = bufData..chair.tabUserAction:GetPackString()
    local myGetCard = 0
    local standCard = chair:GetStandCard()
    if self.CurUserId == chair.UserChairID and #standCard % 3 == 2 then myGetCard = self.CurGetCard end  --当前玩家,且牌未满
    bufData = bufData..string.pack('BB', self.LeftCardCount, myGetCard)  --剩余数量和刚才摸的牌,如果他不是当前玩家,就为0
    if #standCard % 3 == 2 then   --牌满的情况下要去掉 
        local len = #standCard  
        for i = 1, len do
            if standCard[i] == self.CurGetCard then table.remove(standCard, i) end
        end
    end
	for i = 1, TABLECONST.FULL_PLAYER do
		local chair = self.chairAct[i]
		if chair then 
            if chair.UserChairID == wUserChairId then 
                bufData = bufData..string.pack('I2', #standCard)
            else 
                bufData = bufData..string.pack('I2', #chair:GetStandCard())
            end
		else bufData = bufData..string.pack('I2', 0) end 
	end
    
	bufData = bufData..chair:SendMeOfflineBackData('standCard')

	for i = 1, TABLECONST.FULL_PLAYER do
		local chair = self.chairAct[i]
		if chair then 
            bufData = bufData..string.pack('I2', #chair.tabWeaveItem)	
		else bufData = bufData..string.pack('I2', 0) end
	end

	for i = 1, TABLECONST.FULL_PLAYER do
		local chair = self.chairAct[i]
		if chair then 
			bufData = bufData..chair:SendMeOfflineBackData('weaveItem')	
		else bufData = bufData..BufTool.WriteOffset(6 * TABLECONST.MAXWEAVCOUNT) end 
	end
	
	for i = 1, TABLECONST.FULL_PLAYER do
		local chair = self.chairAct[i]
		if chair then 
            bufData = bufData..string.pack('I2', #(chair.tabCbOutCard or {}))
		else bufData = bufData..string.pack('I2', 0) end
	end
	
	for i = 1, TABLECONST.FULL_PLAYER do
		local chair = self.chairAct[i]
		if chair then 
			bufData = bufData..chair:SendMeOfflineBackData('outCard')	
		else bufData = bufData..BufTool.WriteOffset(TABLECONST.MAXOUTCARDCOUNT) end
	end

	for i = 1, TABLECONST.FULL_PLAYER do      --fenghuData
		local chair = self.chairAct[i]
		if chair then
            local bFH = chair:IsBeiFengHu()
            if bFH then bFH = 1 else bFH = 0 end
            bufData = bufData..string.pack('B', bFH)
		else bufData = bufData..string.pack('B', 0) end 
	end
	
    local lenTing = 0
    if chair.tabTingCardData and chair.tabTingCardData.val then lenTing = #chair.tabTingCardData.val end    
    bufData = bufData..string.pack('B', lenTing)
	bufData = bufData..chair:SendMeOfflineBackData('tingData')
    bufData = bufData..BufTool.WriteOffset(128)
    bufData = bufData..string.pack('I4', self.PlayerNum)
	
	chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_OfflineBackData, bufData)
    
    local bufBackNewData = ''
    for i = 1, TABLECONST.FULL_PLAYER do      --fenghuData
		local chair = self.chairAct[i]
		if chair then 
            local bFH = chair:IsBeiFengHu()
            if bFH then bFH = 1 else bFH = 0 end
			bufBackNewData = bufBackNewData..string.pack('B', bFH)
		else bufBackNewData = bufBackNewData..string.pack('B', 0) end 
	end
    bufBackNewData = bufBackNewData..chair:SendMeOfflineBackData('tingData')
    chair:SendDataInLua(CMD.Sub_GameSvrToClient_TableModle_OfflineBackNewData, bufBackNewData)
end


return TableLogic
